#include "pcap_wrapper.h"

#include "utils.h"

#include <pcap/pcap.h>

#define GLOG_USE_GLOG_EXPORT
#include <glog/logging.h>

PcapWrapper::PcapWrapper(const char *devName, const char *filter) : m_dev(devName), m_filter(filter)
{
    m_handle = nullptr;
    m_dumper = nullptr;
}

PcapWrapper::~PcapWrapper()
{
    destroy();
}

void PcapWrapper::gotPacket(unsigned char *args, const struct pcap_pkthdr *header, const unsigned char *packet)
{
    PcapWrapper *pthis   = (PcapWrapper *)args;
    std::string  message = get_hex_string((char *)packet, header->len);
    LOG(INFO) << "获取到数据, 长度: " << header->len << ", 数据: " << message;

    if (pthis->m_dumper != nullptr) {
        pcap_dump((u_char *)pthis->m_dumper, header, packet);
        pcap_dump_flush(pthis->m_dumper);
    }
}

bool PcapWrapper::initialize(const char *savefile)
{
    char               errbuf[1024] = {0};
    struct bpf_program bpf;
    bpf_u_int32        mask;
    bpf_u_int32        net;

    if (pcap_lookupnet(m_dev.c_str(), &net, &mask, errbuf) == -1) {
        LOG(ERROR) << "pcap_lookupnet error: " << errbuf;
        return false;
    }

    m_handle = pcap_open_live(m_dev.c_str(), 65535, 1, 0, errbuf);
    if (m_handle == nullptr) {
        LOG(ERROR) << "pcap_open_live error: " << errbuf;
        return false;
    }
    if (pcap_compile(m_handle, &bpf, m_filter.c_str(), 0, net) == -1) {
        LOG(ERROR) << "pcap_compile error: " << pcap_geterr(m_handle);
        goto err;
    }
    if (pcap_setfilter(m_handle, &bpf) == -1) {
        LOG(ERROR) << "pcap_setfilter error: " << pcap_geterr(m_handle);
        goto err;
    }
    if (savefile) {
        m_dumper = pcap_dump_open(m_handle, savefile);
        if (m_dumper == nullptr) {
            LOG(ERROR) << "pcap_dump_open error: " << pcap_geterr(m_handle);
            goto err;
        }
    }
    return true;

err:
    pcap_close(m_handle);
    m_handle = nullptr;
    return false;
}

void PcapWrapper::pcapTaskThread()
{
    int ret = pcap_loop(m_handle, -1, PcapWrapper::gotPacket, (u_char *)this);
    if (ret != 0) {
        LOG(WARNING) << "pcap_loop: " << pcap_statustostr(ret);
    }
}

void PcapWrapper::run()
{
    m_thread = std::thread(&PcapWrapper::pcapTaskThread, this);
}

void PcapWrapper::destroy()
{
    if (m_thread.joinable()) {
        if (m_handle != nullptr) {
            pcap_breakloop(m_handle);
        }
        m_thread.join();
    }

    if (m_handle != nullptr) {
        pcap_close(m_handle);
        m_handle = nullptr;
    }
    if (m_dumper != nullptr) {
        pcap_dump_close(m_dumper);
        m_dumper = nullptr;
    }
}